#include "gamesettings.h"
#include "ui_gamesettings.h"
#include <QMessageBox>
#include <QDebug>
#include "gamewindow.h"
#include "gameconstants.h"

GameSettings::GameSettings(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::GameSettings)
{
    ui->setupUi(this);

    gameMode = SINGLE;
    connected = false;
    player = HOST;
    rightPlayerIsReady = false;

    player1Name = DEFNAMES.at(rand() % DEFNAMES.size());
    player2Name = DEFNAMES.at(rand() % DEFNAMES.size());
    ui->lineName->setText(player1Name);
    ui->lineName2->setText(player2Name);
    updateWidgetState();
}

GameSettings::~GameSettings()
{
    delete ui;
}

void GameSettings::addToLog(QString msg, QString senderName = "")
{
    if (senderName != "")
    {
        ui->chatDisplay->appendHtml("<b>" + senderName + "</b>: " + msg);
    }
    else
    {
        ui->chatDisplay->appendHtml("--> " + msg);
    }
}

void GameSettings::on_radBtnSing_clicked()
{
    gameMode = SINGLE;
    player = HOST;
    updateWidgetState();
}

void GameSettings::on_radBtnLoc_clicked()
{
    gameMode = LOCAL;
    player = HOST;
    updateWidgetState();
}

void GameSettings::on_radBtnNet_clicked()
{
    gameMode = NETWORK;
    updateWidgetState();
}

void GameSettings::on_lineEnterChat_returnPressed()
{
    QString msg = ui->lineEnterChat->text();

    if (msg.size() == 0) {
        msg = DEFMESSAGES.at(rand() % DEFMESSAGES.size());
    }

    addToLog(msg, player1Name);
    msg = "chatmsg:" + player1Name + ":" + msg;

    if (player == HOST) {
         _server_sendToClient(msg);
    } else if (player == CLIENT) {
        _client_sendToServer(msg);
    }

    ui->lineEnterChat->setText("");
    ui->lineEnterChat->setFocus();
}

void GameSettings::on_lnIpAddress_returnPressed()
{
    on_btnConnectServer_clicked();
}

void GameSettings::on_lineName_editingFinished()
{
    if (ui->lineName->text().size() == 0) {
        ui->lineName->setText(player1Name);
    } else {
        player1Name = ui->lineName->text();
    }
}

void GameSettings::on_lineName2_editingFinished()
{
    if (ui->lineName2->text().size() == 0) {
        ui->lineName2->setText(player2Name);
    } else {
        player2Name = ui->lineName2->text();
    }
}

void GameSettings::on_lineName_returnPressed()
{
    if (ui->lineName->text() != "")
    ui->btnStart->setFocus();
}

void GameSettings::on_lstDifficulty_currentIndexChanged(int index)
{
    if (connected && player == HOST) {
        _server_sendToClient("settings:" + QString::number(index)
                             + ":" + QString::number(ui->lstDuration->currentIndex()));
    }
}

void GameSettings::on_lstDuration_currentIndexChanged(int index)
{
    if (connected && player == HOST) {
        _server_sendToClient("settings:" + QString::number(ui->lstDifficulty->currentIndex())
                             + ":" + QString::number(index));
    }
}

void GameSettings::on_btnConnectServer_clicked()
{
    if (ui->btnConnectServer->text() == "Disconnect")
    {
        socket->disconnectFromHost();
    }
    else
    {
        QString hostname = ui->lnIpAddress->text();
        if (hostname.size() == 0) {
            addToLog("Please specify name of chat server.");
            ui->lnIpAddress->setFocus();
            return;
        }
        ui->btnConnectServer->setText("connecting...");

        thread = new ConnectThread(hostname);
        connect(thread, &QThread::finished, this, &GameSettings::_client_connectFinished);
        thread->start();

        updateWidgetState();
    }
}

void GameSettings::on_btnCreateServer_clicked()
{
    if (ui->btnCreateServer->text() == "Create Server")
    {
        if (!server.listen(QHostAddress::Any, 5000))
        {
            addToLog("Cannot start socket.");
            return;
        }

        connect(&server, SIGNAL(newConnection()), this, SLOT(_server_clientConnected()));

        addToLog("Server created. Waiting for other player to connect...");

        player = HOST;
        ui->btnCreateServer->setText("Disconnect");

        updateWidgetState();
    }
    else
    {
        _server_sendToClient("disconnect");

        server.close();
        server.disconnect();

        ui->btnCreateServer->setText("Create Server");
        updateWidgetState();

        if (!connected) { addToLog("Server deleted."); }
    }
}

void GameSettings::on_btnStart_clicked()
{
    if (player == HOST)
    {
        _difficulty_ = (std::vector<Difficulty>{CHEAT, EASY, MEDIUM, HARD, IMPOSSIBLE})
                .at(ui->lstDifficulty->currentIndex());
        duration = ui->lstDuration->currentIndex() + 1;

        if (gameMode == NETWORK && connected) {
            _server_sendToClient("ingame:name:" + player1Name + ":" + player2Name);
            _server_sendToClient("start");
        } else if (gameMode == SINGLE) {
            player2Name = DEFNAMES.at(rand() % DEFNAMES.size());
        }

        emit gameStarted();

    }
    else if (connected && player == CLIENT)
    {
        rightPlayerIsReady = true;
        QString msg = player1Name + " is ready";
        addToLog(msg);
        _client_sendToServer("log:" + msg);
        _client_sendToServer("ready:" + player1Name);

        updateWidgetState();
    }
}

// enables or disables the widgets according to the current settings
void GameSettings::updateWidgetState()
{
    if (gameMode == SINGLE)
    {
        ui->btnCreateServer->setEnabled(false);
        ui->btnConnectServer->setEnabled(false);
        ui->lnIpAddress->setEnabled(false);
        ui->lineEnterChat->setEnabled(false);
        ui->radBtnSing->setEnabled(true);
        ui->radBtnLoc->setEnabled(true);
        ui->lstDifficulty->setEnabled(true);
        ui->lstDuration->setEnabled(true);
        ui->btnStart->setEnabled(true);
        ui->lineName->setEnabled(true);
        ui->lineName2->setEnabled(false);
        ui->lineName->setFocus();
    }
    else if (gameMode == LOCAL)
    {
        ui->btnCreateServer->setEnabled(false);
        ui->btnConnectServer->setEnabled(false);
        ui->lnIpAddress->setEnabled(false);
        ui->lineEnterChat->setEnabled(false);
        ui->radBtnSing->setEnabled(true);
        ui->radBtnLoc->setEnabled(true);
        ui->lstDifficulty->setEnabled(true);
        ui->lstDuration->setEnabled(true);
        ui->btnStart->setEnabled(true);
        ui->lineName->setEnabled(true);
        ui->lineName2->setEnabled(true);
        ui->lineName2->setFocus();
    }
    else if (gameMode == NETWORK)
    {
        ui->lineName2->setEnabled(false);

        if (!connected)
        {
            ui->lineEnterChat->setEnabled(false);
            ui->btnStart->setEnabled(false);
            ui->radBtnSing->setEnabled(true);
            ui->radBtnLoc->setEnabled(true);
            ui->lstDifficulty->setEnabled(true);
            ui->lstDuration->setEnabled(true);
            ui->lineName->setEnabled(true);

            if (ui->btnCreateServer->text() == "Disconnect") {
                ui->btnConnectServer->setEnabled(false);
                ui->lnIpAddress->setEnabled(false);
                ui->btnCreateServer->setEnabled(true);
            } else if (ui->btnConnectServer->text() == "connecting...") {
                ui->btnConnectServer->setEnabled(false);
                ui->lnIpAddress->setEnabled(false);
                ui->btnCreateServer->setEnabled(false);
            } else {
                ui->btnCreateServer->setEnabled(true);
                ui->btnConnectServer->setEnabled(true);
                ui->lnIpAddress->setEnabled(true);
                ui->lnIpAddress->selectAll();
                ui->lnIpAddress->setFocus();
            }
        }
        else if (connected)
        {
            ui->radBtnLoc->setEnabled(false);
            ui->radBtnSing->setEnabled(false);
            ui->lnIpAddress->setEnabled(false);

            if (player == HOST)
            {
                if (rightPlayerIsReady) {
                    ui->btnStart->setEnabled(true);
                } else if (!rightPlayerIsReady) {
                    ui->btnStart->setEnabled(false);
                }

                ui->lstDifficulty->setEnabled(true);
                ui->lstDuration->setEnabled(true);
                ui->btnCreateServer->setEnabled(true);
                ui->lineName->setEnabled(true);
                ui->btnConnectServer->setEnabled(false);

            }
            else if (player == CLIENT)
            {
                if (rightPlayerIsReady) {
                    ui->btnStart->setEnabled(false);
                    ui->lineName->setEnabled(false);
                } else if (!rightPlayerIsReady) {
                    ui->btnStart->setEnabled(true);
                    ui->lineName->setEnabled(true);
                }

                ui->lstDifficulty->setEnabled(false);
                ui->lstDuration->setEnabled(false);
                ui->btnConnectServer->setEnabled(true);
                ui->btnCreateServer->setEnabled(false);
            }

            ui->lineEnterChat->setEnabled(true);
            ui->lineEnterChat->setFocus();
        }

    }
}


void GameSettings::_server_clientConnected()
{
    QTcpSocket *sock = server.nextPendingConnection();
    connect(sock, SIGNAL(disconnected()), this, SLOT(_server_clientDisconnected()));
    connect(sock, SIGNAL(readyRead()), this, SLOT(_server_dataReceived()));

    connected = true;

    QApplication::processEvents();
    _server_sendToClient("settings:"
                         + QString::number(ui->lstDifficulty->currentIndex())
                         + ":" + QString::number(ui->lstDuration->currentIndex()));

    updateWidgetState();
}

void GameSettings::_server_dataReceived()
{
    QTcpSocket *sock = dynamic_cast<QTcpSocket*>(sender());

    while (sock->canReadLine())
    {
        QString str = sock->readLine().trimmed();

        if (str.startsWith("chatmsg:"))
        {
            QString msg = str.mid(str.indexOf(':') + 1);
            addToLog(msg.mid(msg.indexOf(':') + 1), msg.left(msg.indexOf(':')));
        }
        else if (str.startsWith("log:"))
        {
            addToLog(str.mid(4));
        }
        else if (str.startsWith("ready:"))
        {
            rightPlayerIsReady = true;
            player2Name = str.mid(6);
            updateWidgetState();
        }
        else if (str.startsWith("ingame:"))
        {
            emit toGameWindow_ServerReceivedData(str.mid(7));
        }
    }
}

void GameSettings::_server_clientDisconnected()
{
    QTcpSocket *sock = dynamic_cast<QTcpSocket*>(sender());
    sock->deleteLater();
    connected = false;
    rightPlayerIsReady = false;

    addToLog("Connection lost.");
    updateWidgetState();

    emit toGameWindow_connectionLost();
}

void GameSettings::_server_sendToClient(QString msg)
{
    if (connected) {
        for (QObject *obj : server.children())
        {
            QTcpSocket* anotherSock = dynamic_cast<QTcpSocket*>(obj);
            if (anotherSock != NULL)
            {
                msg = msg + "\n";
                anotherSock->write(msg.toLatin1());
            }
        }
    }
}

void GameSettings::_client_dataReceived()
{
    while (socket->canReadLine())
    {
        QString str = socket->readLine().trimmed();

        if (str.startsWith("chatmsg:"))
        {
            QString msg = str.mid(str.indexOf(':') + 1);
            addToLog(msg.mid(msg.indexOf(':') + 1), msg.left(msg.indexOf(':')));
        }
        else if (str.startsWith("log:"))
        {
            addToLog(str.mid(4));
        }
        else if (str.startsWith("settings:"))
        {
            str = str.mid(str.indexOf(':') + 1);
            ui->lstDifficulty->setCurrentIndex(str.left(str.indexOf(':')).toInt());
            ui->lstDuration->setCurrentIndex(str.mid(str.indexOf(':') + 1).toInt());
        }
        else if (str.startsWith("disconnect"))
        {
            socket->disconnectFromHost();
        }
        else if (str.startsWith("start"))
        {
            emit gameStarted();
        }
        else if (str.startsWith("ingame:"))
        {
            emit toGameWindow_ClientReceivedData(str.mid(7));
        }
    }
}

void GameSettings::_client_disconnectedFromServer()
{
    addToLog("Connection lost.");
    ui->btnConnectServer->setText("Connect to Server");
    connected = false;
    rightPlayerIsReady = false;

    socket->deleteLater();
    updateWidgetState();

    emit toGameWindow_connectionLost();
}

void GameSettings::_client_connectFinished()
{
    if (thread->getSuccess())
    {
        socket = thread->getSocket();

        player = CLIENT;

        connect(socket, SIGNAL(readyRead()), this, SLOT(_client_dataReceived()));
        connect(socket, SIGNAL(disconnected()), this, SLOT(_client_disconnectedFromServer()));

        connected = true;
        ui->btnConnectServer->setText("Disconnect");

        QString msg = player1Name + " connected to server.";
        addToLog(msg);
        _client_sendToServer("log:" + msg);

        updateWidgetState();
    }
    else
    {
        addToLog(thread->getErrorMsg());
        ui->btnConnectServer->setText("Connect to Server");
        updateWidgetState();
    }
}

void GameSettings::_client_sendToServer(QString msg)
{
    if (connected) {
        msg = msg + "\n";
        socket->write(msg.toStdString().c_str());
    }
}



void ConnectThread::run()
{
    socket = new QTcpSocket();

    socket->connectToHost(hostname, 5000);
    if (!socket->waitForConnected())  {
        success = false;
        errorMsg = "Unable to connect to server.";
        return;
    }

    socket->moveToThread(QApplication::instance()->thread());
}
